package game;

import evaluator.ElectricalResistanceFinder;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.SwingConstants;
import javax.swing.border.BevelBorder;

import players.HexGameGui;
import players.Player;

public class HexGameFrame extends JFrame implements ActionListener, MouseListener  {

  private static final long serialVersionUID = 1L;
  
  
  public static class MyButtonGroup extends ButtonGroup {
    public int getIndex() {
      for (int i = 0; i < buttons.size(); i++)
        if (buttons.get(i).isSelected()) return i;
    return -1;
    }
  }

  
  public static MyButtonGroup traceButtonGroup;
  
  public static MyButtonGroup[] strategyButtonGroups = new MyButtonGroup[2];

  public static boolean isDark(Color clr) {
    int red = clr.getRed();
    int green = clr.getGreen();
    int blue = clr.getBlue();
    return (red + blue) < 425 && green < 200 && (red + green + blue) < 530;
  }
  
  
  public static boolean strategyIsDefense(int whichPlayer) {
    return strategyButtonGroups[whichPlayer-1].getIndex() == 1;
  }


  public static boolean strategyIsOffense(int whichPlayer) {
    return strategyButtonGroups[whichPlayer-1].getIndex() == 0;
  }

  // End of static declarations // 
  
  

  private HexBoardPanel hexBoardPanel;
  private JButton undoBtn, nextMoveBtn, resignBtn, newGameBtn, newMatchBtn, exitBtn; 
  private JCheckBox evalBtn;
  private final String blanks = "                         ";
  private final JLabel nextPlyrField = new JLabel(blanks);
  private final JLabel winnerField = new JLabel(blanks);
  private final HexGame hexGame;
  private final HexBoard hexBoard;
  private final JLabel[] plyrResistance = {new JLabel(blanks, SwingConstants.RIGHT), 
                                           new JLabel(blanks, SwingConstants.RIGHT)};
  private final JLabel[] plyrMatchScores;

  
  public HexGameFrame(Player player1, Player player2) {
    this(new JLabel[]{new JLabel("Wins: 0", SwingConstants.RIGHT), 
                      new JLabel("Wins: 0", SwingConstants.RIGHT)}, 
         player1, 
         player2,
         -1);
  }

  public HexGameFrame(JLabel[] plyrMatchScores, Player player1, Player player2, int newBoardSize) {
    
    this.plyrMatchScores = plyrMatchScores;
    
    newBoardSize = getBoardSize(newBoardSize);
    if (newBoardSize == -1) {
      // If user cancelled, quit. 
      // hexBoard and hexGame are final. Set them to satisfy eclipse.
      hexBoard = null;
      hexGame = null;
      return;
    }
    this.hexBoard = 
      new HexBoard(newBoardSize, setPlayerPositions(player1, player2), this);
    this.hexGame = hexBoard.hexGame ;
        
    setLayout(new BorderLayout());

    add(makeBoardPanel(), BorderLayout.CENTER) ;
    add(makeLeftPanel(), BorderLayout.WEST);

    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setSize(900, 600); 
    setTitle("Hex");

    setVisible(true); 
    hexBoardPanel.hexBoardImage.initializeImage();

    hexGame.makeFirstMove();
    setNextPlayer();
  }


  @Override
  public void actionPerformed(ActionEvent e) {
    if (e.getSource() == newMatchBtn) {  // New match
      if (resignBtn.isEnabled() &&
              !areYouSure(hexGame.getCurrentPlayer().getName() + 
                          ", are you sure you want to start a new match?")) return;
      dispose();
      HexGameGui.main(null);
      return;
    }
    
    if (e.getSource() == evalBtn) { // Evaluate evalBtn CheckBox
      displayResistances();     
      return;
    }

    if (e.getSource() == newGameBtn) { // New Game
      if (resignBtn.isEnabled() &&
          !areYouSure(hexGame.getCurrentPlayer().getName() + 
                      ", are you sure you want to start a new game?")) return;
      newGame();
      return;
    }
      
    if (e.getSource() == exitBtn) {  // Quit
      if (resignBtn.isEnabled() &&
              !areYouSure(hexGame.getCurrentPlayer().getName() + 
                          ", are you sure you want to exit?")) return;
      System.exit(0);
    }
          
    boolean setNextPlayer = true;
    
    if (e.getSource() == undoBtn) {  // Back one Move
      if (hexBoard.undoableMove()) {
        hexBoard.backOneMove();
        displayResistances();
      }
      else {
        newGame();
        setNextPlayer = false;
      }
    }

    if (e.getSource() == nextMoveBtn) hexGame.proceed();  // NextMoveButton

    // No need for "Are you sure." That will be asked when the resign move is made.
    if (e.getSource() == resignBtn) hexGame.resign();  // Resign 
    
    // At this point the player has moved as a result of the call to hexGame.proceed().
    if (setNextPlayer) setNextPlayer();

  }
  
  
  /** 
   * @param what - the question to ask the user.
   * @return true if the user answers the question "Yes"; false otherwise.
   */
  public boolean areYouSure(String what) {
    return  JOptionPane.showConfirmDialog(
            null,
            what, 
            null, 
            JOptionPane.YES_NO_OPTION) == 0;
  }


	public void displayResistances() {
		HexBoard boardClone = this.hexBoard.clone();
		for (int i : new int[] { 1, 2 }) {
			ElectricalResistanceFinder e = 
			  new ElectricalResistanceFinder(boardClone, boardClone.getPlayer(i));
			plyrResistance[i - 1].setText(""
					+ (evalBtn.isSelected() ? (int)e.getEqResistance() : ""));
		}
	}


  private void enableButtons(boolean hasWinner) {
    undoBtn.setEnabled(!hasWinner);
    nextMoveBtn.setEnabled(!hasWinner);
    resignBtn.setEnabled(!hasWinner);
  }


  private int getBoardSize(int newBoardSize) {
    
    if (newBoardSize == -1) {
      // Generate list of sizes for user to select from.
      int minBoardSize = 3;
      Object[] sizes = new Object[27-minBoardSize];
      for (int i = minBoardSize; i <= 26; i++) sizes[i-minBoardSize] = i;

      Object ptr = JOptionPane.showInputDialog(null, "Select board size", "Input",
                                               JOptionPane.INFORMATION_MESSAGE, null,
                                               sizes, sizes[minBoardSize+1]);
      // ptr will be null if user hit cancel.
      // If ptr is null, fall through to return newBoardSize, which is -1.
       if (ptr != null) return (Integer) ptr; 
    }
    return newBoardSize;
  }


  public JLabel[] getPlyrMatchScores() { return plyrMatchScores; }


  private JPanel makeBoardPanel() {
    JPanel boardPnl = new JPanel(new BorderLayout());
    boardPnl.addMouseListener(this);
    boardPnl.add(makeButtonsPanel(), BorderLayout.SOUTH);
    this.hexBoardPanel = new HexBoardPanel(hexBoard, nextMoveBtn) ; 
    boardPnl.addMouseMotionListener(hexBoardPanel);
    boardPnl.add(hexBoardPanel, BorderLayout.CENTER) ;
    boardPnl.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
    return boardPnl;	  
  }


  private JPanel makeButtonsPanel() {
    JPanel buttonsPnl = new JPanel();
    buttonsPnl.setLayout(new BoxLayout(buttonsPnl, BoxLayout.X_AXIS));
    buttonsPnl.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));

    // Create buttons
    undoBtn = new JButton("Back One Move");
    nextMoveBtn = new JButton("Next Move");
    resignBtn = new JButton("Resign");
    newGameBtn = new JButton("New Game");
    newMatchBtn = new JButton("New Match");
    exitBtn = new JButton("Exit");

    // Add buttons to the panel & add action listeners to the buttons
    JPanel pnl1 = new JPanel(new GridLayout(1, 2));
    JPanel pnl2 = new JPanel(new GridLayout(1, 4));
    buttonsPnl.add(pnl1);
    buttonsPnl.add(new JLabel("   "));
    buttonsPnl.add(pnl2);
    pnl1.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
    pnl2.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
    pnl1.setBackground(Color.lightGray);
    pnl2.setBackground(Color.lightGray);

    AbstractButton[] buttons = {undoBtn, nextMoveBtn, resignBtn, newGameBtn, newMatchBtn, exitBtn};
    for (int i = 0; i < buttons.length; i++) {
      buttons[i].setFocusable(false);
      buttons[i].addActionListener(this);
      (i < 2 ? pnl1 : i < 6 ? pnl2 : buttonsPnl).add(buttons[i]);
    }    
    return buttonsPnl;
  }

  
  private void makeControls(JPanel leftPanel) {
    JPanel p = new JPanel(new GridLayout(2, 1));
    p.add(new JLabel());
    evalBtn = new JCheckBox("Show resistances", true) ;
    p.add(evalBtn);
    evalBtn.addActionListener(this);
    leftPanel.add(p);
    leftPanel.add(makeTracePanel());
  }

  
  private JPanel makeLeftPanel() {
    JPanel leftPanel = new JPanel(new GridLayout(7, 1));
    leftPanel.setBorder(BorderFactory.createEmptyBorder(30,20,10,20));

    makePlayerPanels(leftPanel);

    makeControls(leftPanel);
      
    return leftPanel;
  }


  private void makePlayerPanels(JPanel leftPanel) {
    JLabel[] lbls = {plyrResistance[0], plyrResistance[1], nextPlyrField, winnerField};
    for (int i = 0 ; i < 3 ; i++) lbls[i].setOpaque(true);
    String[] labels = {hexBoard.getPlayer(1).getName(), hexBoard.getPlayer(2).getName(), 
                       "Next Move", "Winner"};
    
    JPanel[] pnl = new JPanel[6];
    
    for (int i = 0 ; i < 2 ; i++) {
      pnl[i] = new JPanel(new GridLayout(3, 1));
      pnl[i].add(plyrMatchScores[i]);
      pnl[i].add(lbls[i]);
      Color color = hexBoard.getPlayer(i+1).getColor();
      lbls[i].setBackground(color);
      if (isDark(color)) lbls[i].setForeground(Color.WHITE);
      pnl[i].add(makeStrategyPanel(i+1)); // For player(i+1)
      pnl[i].setBorder(BorderFactory.createTitledBorder(labels[i]));
      leftPanel.add(pnl[i]);
    }
    
    JPanel p = new JPanel(new GridLayout(2, 1));
    for (int i = 2 ; i < 4 ; i++) {
      pnl[i] = new JPanel(new GridLayout(1, 1));
      pnl[i].add(lbls[i]);
      lbls[i].setOpaque(true);
      pnl[i].setBorder(BorderFactory.createTitledBorder(labels[i]));
      p.add(pnl[i]);
    }
    leftPanel.add(p);
  }

  
  private JPanel makeStrategyPanel(int whichPlayer) {
    JPanel strategyPanel = new JPanel(new GridLayout(1, 3));
    strategyButtonGroups[whichPlayer-1] = new MyButtonGroup();
    JRadioButton[] strategies = {new JRadioButton("O"), 
                                 new JRadioButton("D"), 
                                 new JRadioButton("C")};
    for (int i = 0; i < strategies.length; i++) {
      JRadioButton jrb = strategies[i];
      // Set player 1 to play offence and player 2 to play defense.
      jrb.setSelected(i == whichPlayer-1);
      jrb.setFocusable(false);
      strategyButtonGroups[whichPlayer-1].add(jrb);
      strategyPanel.add(jrb);
    }
    return strategyPanel;
  }


  private JPanel makeTracePanel() {
    JPanel p = new JPanel(new GridLayout(2, 1));
    p.add(new JLabel());
    JPanel tracePanel = new JPanel(new GridLayout(1, 1));
    tracePanel.setBorder(BorderFactory.createLineBorder(Color.BLACK)); 
    tracePanel.setBorder(BorderFactory.createTitledBorder("Trace depth"));

    int buttonCount = 3;
    JPanel buttonPanel = new JPanel(new GridLayout(1, 3));
    tracePanel.add(buttonPanel);
    traceButtonGroup = new MyButtonGroup();
    for (int i = 0; i < buttonCount; i++) {
      JRadioButton jrb = new JRadioButton(i+"");
      jrb.setSelected(i == 0);
      traceButtonGroup.add(jrb);
      buttonPanel.add(jrb);
    }
    p.add(tracePanel);
    return p;
  }


  @Override
  public void mouseClicked(MouseEvent e) {
    if (!nextMoveBtn.isEnabled()) return;
    Point p = new Point(e.getX(), e.getY());
    
    Cell selectedcell = hexBoardPanel.cellThatContainsPoint(p);
    if (selectedcell != null && hexBoard.isEmpty(selectedcell)) {
      hexGame.proceedManually(selectedcell);
      setNextPlayer();
    }
  }


  @Override
  public void mouseEntered(MouseEvent e) { /* */ }


  @Override
  public void mouseExited(MouseEvent e) { /* */ }


  @Override
  public void mousePressed(MouseEvent e) { /* */ }


  @Override
  public void mouseReleased(MouseEvent e) { /* */ }


  private void newGame() {
    hexBoard.newGame();
    dispose();
  }


  private Player[] setPlayerPositions(Player player1, Player player2) {
    Player[] players;
    if (Math.random() > 0.5) {
      String p1Name = player1.getName();
      player1.setName(player2.getName());
      player2.setName(p1Name);
      JLabel temp = plyrMatchScores[0];
      plyrMatchScores[0] = plyrMatchScores[1];
      plyrMatchScores[1] = temp;
      players = new Player[]{player2, player1};
    } else players = new Player[]{player1, player2} ;
    return players;
  }


  private void setNextPlayer() {

    Player winner = this.hexBoard.getWinner();		
    boolean hasWinner = winner != null;

    hexGame.switchPlayers();
    
    // nextPlayer is used only if !hasWinner.
    Player nextPlayer = hexGame.getCurrentPlayer();

    this.nextPlyrField.setBackground(hasWinner ? Color.WHITE : nextPlayer.getColor());		
    this.winnerField.setBackground(hasWinner ? winner.getColor() : Color.WHITE);

    this.nextMoveBtn.setEnabled(!hasWinner);
    if (hasWinner) updateMatchScores(winner);
    enableButtons(hasWinner);
    hexBoardPanel.repaint() ;
  }


  private void updateMatchScores(Player winner) {
    int whichPlayerScore = winner == hexBoard.getPlayer(1) ? 0 : 1;
    String score = plyrMatchScores[whichPlayerScore].getText();
    int currentScore = Integer.valueOf(score.substring(score.lastIndexOf(" ")+1));
    plyrMatchScores[whichPlayerScore].setText("Wins: " + (currentScore + 1));    
  }

}
